using System;
using System.Collections.Generic;
using JigLibX.Geometry;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace SolarWinds.Shapes
{
    public class ModelShape : Shape
    {
        private readonly String mModelAsset;
        protected Model mModel;
        public Model Model
        {
            get { return mModel; }
        }

        public ModelShape(Game game, String assetName)
            : base(game)
        {
            mModelAsset = assetName;
        }

        protected override void LoadContent()
        {
            mModel = Game.Content.Load<Model>(@"Models\" + mModelAsset);

            base.LoadContent();
        }

        public override void  Draw(GameTime gameTime)
        {
 	        base.Draw(gameTime);
        
            foreach (ModelMesh mesh in mModel.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();

                    effect.World = World;
                    effect.View = View;
                    effect.Projection = Projection;
                }
                mesh.Draw();
            }
        }

        protected override BoundingSphere UpdateBoundingSphere()
        {
            if (mModel != null)
            {
                // The models have to be processed by a content 
                // processor that writes their BoudningSpheres to the tag.
                return (BoundingSphere)((object[])mModel.Tag)[1];
            }

            return new BoundingSphere();
        }

        protected override BoundingBox UpdateBoundingBox()
        {
            if (mModel != null)
            {
                // The models have to be processed by a content 
                // processor that writes their BoudningBox to the tag.
                return (BoundingBox)((object[])mModel.Tag)[0];
            }

            return new BoundingBox();
        }

        public void ExtractData (List<Vector3> vertices, List<TriangleVertexIndices> indices, Matrix orientation)
        {
            ExtractData(vertices, indices, mModel, orientation);
        }

        /// <summary>
        /// Helper Method to get the vertex and index List from the model.
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="indices"></param>
        /// <param name="model"></param>
        /// <param name="orientation"></param>
        private static void ExtractData(List<Vector3> vertices, List<TriangleVertexIndices> indices, Model model, Matrix orientation)
        {
            var bones = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(bones);
            foreach (ModelMesh mm in model.Meshes)
            {
                Matrix xform = bones[mm.ParentBone.Index] * orientation; // Pre-rotate the trianglemesh
                foreach (ModelMeshPart mmp in mm.MeshParts)
                {
                    int offset = vertices.Count;
                    var a = new Vector3[mmp.NumVertices];
                    mm.VertexBuffer.GetData(mmp.StreamOffset + mmp.BaseVertex * mmp.VertexStride,
                        a, 0, mmp.NumVertices, mmp.VertexStride);
                    for (int i = 0; i != a.Length; ++i)
                        Vector3.Transform(ref a[i], ref xform, out a[i]);
                    vertices.AddRange(a);

                    if (mm.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits)
                        throw new Exception(
                            String.Format("Model uses 32-bit indices, which are not supported."));
                    var s = new short[mmp.PrimitiveCount * 3];
                    mm.IndexBuffer.GetData(mmp.StartIndex * 2, s, 0, mmp.PrimitiveCount * 3);
                    var tvi = new TriangleVertexIndices[mmp.PrimitiveCount];
                    for (int i = 0; i != tvi.Length; ++i)
                    {
                        tvi[i].I0 = s[i * 3 + 2] + offset;
                        tvi[i].I1 = s[i * 3 + 1] + offset;
                        tvi[i].I2 = s[i * 3 + 0] + offset;
                    }
                    indices.AddRange(tvi);
                }
            }
        }

    }
}